﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MonopolyNOHI2015
{
    public class Spel
    {
        // Properties
        private Bord _monopolyBord;
        private List<Speler> _spelers;
        private List<Dobbelsteen> _dobbelstenen;
        private Bank _heraBank;
        private List<Eigendomsbewijs> _eigendomsbewijzen;

        // Constructor
        public Spel()
        {
            MaakDobbelstenen();
            MaakMonopolyBord();
            MaakSpelers();
            MaakBank();
            MaakEigendomsbewijzen();
        }

        private void MaakDobbelstenen()
        {
            var random = new Random();
            _dobbelstenen = new List<Dobbelsteen>(2)
            {
                new Dobbelsteen(random),
                new Dobbelsteen(random)
            };
        }

        private void MaakMonopolyBord()
        {
            _monopolyBord = new Bord();
        }

        private void MaakSpelers()
        {
            _spelers = new List<Speler>(6)
            {
                new Speler("Herman", 850, _monopolyBord),
                new Speler("Randy", 850, _monopolyBord),
                new Speler("Richard", 850, _monopolyBord),
                new Speler("Guus Geluk", 850, _monopolyBord),
                new Speler("Pech Vogel", 850, _monopolyBord),
                new Speler("Loser", 850, _monopolyBord)
            };
        }

        private void MaakBank()
        {
            _heraBank = new Bank();
        }

        private void MaakEigendomsbewijzen()
        {
            _eigendomsbewijzen = new List<Eigendomsbewijs>(28)
            {
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[1], 60, 2, 10, 30, 90, 160, 250),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[3], 60, 4, 20, 60, 180, 320, 450),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[6], 100, 6, 30, 90, 270, 400, 550),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[8], 100, 6, 30, 90, 270, 400, 550),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[9], 120, 8, 40, 100, 300, 450, 600),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[11], 140, 10, 50, 150, 450, 625, 750),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[13], 140, 10, 50, 150, 450, 625, 750),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[14], 160, 12, 60, 180, 500, 700, 900),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[16], 180, 140, 70, 200, 550, 750, 950),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[18], 180, 140, 70, 200, 550, 750, 950),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[19], 200, 16, 80, 220, 600, 800, 1000),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[21], 220, 18, 90, 250, 700, 875, 1050),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[23], 220, 18, 90, 250, 700, 875, 1050),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[24], 240, 20, 100, 300, 750, 925, 1100),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[26], 260, 22, 110, 330, 800, 975, 1150),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[27], 260, 22, 110, 330, 800, 975, 1150),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[29], 280, 24, 120, 360, 850, 1025, 1200),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[31], 300, 26, 130, 390, 900, 1100, 1275),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[32], 300, 26, 130, 390, 900, 1100, 1275),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[34], 320, 28, 150, 450, 1000, 1200, 1400),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[37], 350, 35, 175, 500, 1100, 1300, 1500),
                new EigendomsbewijsStraat(null, _monopolyBord.Velden[39], 400, 50, 200, 600, 1400, 1700, 2000),
                new EigendomsbewijsStation(null, _monopolyBord.Velden[5], 200, 25, 50, 100, 200),
                new EigendomsbewijsStation(null, _monopolyBord.Velden[15], 200, 25, 50, 100, 200),
                new EigendomsbewijsStation(null, _monopolyBord.Velden[25], 200, 25, 50, 100, 200),
                new EigendomsbewijsStation(null, _monopolyBord.Velden[35], 200, 25, 50, 100, 200),
                new EigendomsbewijsNuts(null, _monopolyBord.Velden[28], 150, 4, 10),
                new EigendomsbewijsNuts(null, _monopolyBord.Velden[12], 150, 4, 10)
            };
        }

        public Bord GetBord()
        {
            return _monopolyBord;
        }

        public List<Speler> GetSpelersList()
        {
            return _spelers;
        }

        public List<Eigendomsbewijs> GetEigendomsbewijsList()
        {
            return _eigendomsbewijzen;
        }

        public List<Dobbelsteen> GetDobbelstenen()
        {
            return _dobbelstenen;
        }

        public Eigendomsbewijs ZoekVeldEigendomsbewijs(Veld veld)
        {
            foreach (var eigendomsbewijs in _eigendomsbewijzen)
            {
                if (eigendomsbewijs.GetVeld() == veld)
                {
                    return eigendomsbewijs;
                }
            }
            return null;
        }

        public bool SpelerBeslistOmVeldTeKopen(Speler s, Eigendomsbewijs eigendomsbewijs)
        {
            // Heeft de speler voldoende geld om het veld te kunnen kopen?
            return s.GetSaldo() >= eigendomsbewijs.GetKoopbedrag();
        }

        public void BepaalEnBetaalHuurStraatVeld(Eigendomsbewijs eigendomsbewijs, int aantalStratenPerStad,
            int straatEigendommen, Speler s, Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsStraat))
            {
                // Vraag de huur voor de straat waarop de huidige speler zich bevindt
                //int aantalHotelsPerStraat = eigendomsbewijs.GetVeld().GetAantalHotels() (nog bouwen!!!)
                var eigendomsbewijsStraat = (EigendomsbewijsStraat)eigendomsbewijs;
                var straatVeld = (StraatVeld)eigendomsbewijsStraat.GetVeld();
                int aantalHuizen = straatVeld.AantalHuizen;
                int aantalHotels = straatVeld.AantalHotels;
                var huurBedrag = (eigendomsbewijsStraat.GetHuur(aantalHuizen, aantalHotels));

                // Dubbele huur zodra de stad compleet is
                if (aantalStratenPerStad - straatEigendommen == 0 && aantalHuizen == 0 && aantalHotels == 0)
                {
                    huurBedrag *= 2;
                }
                Console.WriteLine(s.GetNaam() + " heeft " + huurBedrag + " euro huur betaald aan " + veldEigenaar.GetNaam() + " voor straat " + eigendomsbewijs.GetVeldNaam());
                s.BetaalBedrag(huurBedrag);
                veldEigenaar.OntvangBedrag(huurBedrag);
            }
        }

        public void BepaalEnBetaalHuurStationVeld(Eigendomsbewijs eigendomsbewijs, int stationEigendommen, Speler s,
            Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsStation))
            {
                // Vraag de huur voor het aantal stations dat de speler in bezit heeft
                var eigendomsbewijsStation = (EigendomsbewijsStation)eigendomsbewijs;
                var huurBedrag = eigendomsbewijsStation.GetHuur(stationEigendommen);
                Console.WriteLine(s.GetNaam() + " heeft " + huurBedrag + " euro huur betaald aan " + veldEigenaar.GetNaam() + " voor station " + eigendomsbewijs.GetVeldNaam());
                s.BetaalBedrag(huurBedrag);
                veldEigenaar.OntvangBedrag(huurBedrag);
            }
        }

        public void BepaalEnBetaalHuurNutsVeld(Eigendomsbewijs eigendomsbewijs, int nutsEigendommen, Speler s,
            Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsNuts))
            {
                // Vraag de huur voor het aantal nutsbedrijven dat de speler in bezit heeft
                var eigendomsbewijsNuts = (EigendomsbewijsNuts)eigendomsbewijs;
                var huurBedrag = eigendomsbewijsNuts.GetHuur(nutsEigendommen);
                Console.WriteLine(s.GetNaam() + " heeft " + huurBedrag + " euro huur betaald aan " + veldEigenaar.GetNaam() + " voor nuts " + eigendomsbewijs.GetVeldNaam());
                s.BetaalBedrag(huurBedrag);
                veldEigenaar.OntvangBedrag(huurBedrag);
            }
        }

        public void SpelerBeslistEnMaaktAankoopStraat(Speler s, Eigendomsbewijs eigendomsbewijs)
        {
            if (SpelerBeslistOmVeldTeKopen(s, eigendomsbewijs))
            {
                Console.WriteLine(s.GetNaam() + " heeft " + eigendomsbewijs.GetVeldNaam() + " gekocht voor " + eigendomsbewijs.GetKoopbedrag() + " euro");
                s.KoopEigendomsbewijs(eigendomsbewijs);
            }
        }

        public bool CheckOfStationOpVeldZelfdeIsAlsOpEigendomsbewijs(Eigendomsbewijs eigendomsbewijs, Veld veld, Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsStation) && (veld.GetType() == typeof(StationVeld) && eigendomsbewijs.GetEigenaar() == veldEigenaar))
            {
                return true;
            }

            return false;
        }

        public bool CheckOfNutsOpVeldZelfdeIsAlsOpEigendomsbewijs(Eigendomsbewijs eigendomsbewijs, Veld veld, Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsNuts) && (veld.GetType() == typeof(NutsVeld) && (eigendomsbewijs.GetEigenaar() == veldEigenaar)))
            {
                return true;
            }

            return false;
        }

        public bool CheckOfStadOpVeldZelfdeIsAlsOpEigendomsbewijs(Eigendomsbewijs eigendomsbewijs, Veld veld, Speler veldEigenaar)
        {
            if (eigendomsbewijs.GetType() == typeof(EigendomsbewijsStraat) && (veld.GetType() == typeof(StraatVeld) && (eigendomsbewijs.GetEigenaar() == veldEigenaar)))
            {
                var eigendomsbewijsVeld = (StraatVeld)eigendomsbewijs.GetVeld();
                var pionStraatVeld = (StraatVeld)veld;
                return pionStraatVeld.GetStadNaam() == eigendomsbewijsVeld.GetStadNaam();
            }
            return false;
        }

        public int TelAantalStratenVanHuidigeStad(Veld pionVeld)
        {
            var aantalStraten = 0;
            if (pionVeld.GetType() == typeof(StraatVeld))
            {
                var pionStraatVeld = (StraatVeld)pionVeld;
                foreach (var veld in _monopolyBord.Velden)
                {
                    //Casting kan alleen op type StraatVeld
                    if (veld.GetType() == typeof(StraatVeld))
                    {
                        var straatVeld = (StraatVeld)veld;
                        if (straatVeld.GetStadNaam() == pionStraatVeld.GetStadNaam())
                        {
                            aantalStraten++;
                        }
                    }
                }
            }
            return aantalStraten;
        }

        public void BetaalHuurAanEigenaar(Speler veldEigenaar, Veld nieuwPionVeld, Eigendomsbewijs eigendomsbewijs, Speler s)
        {
            var straatEigendommen = 0;
            var stationEigendommen = 0;
            var nutsEigendommen = 0;

            // Bepaal hoeveel staten de stad heeft waar de pion nu op staat indien het een straatveld is
            var aantalStratenPerStad = TelAantalStratenVanHuidigeStad(nieuwPionVeld);

            // Bepaal hoeveel straten eigendommen de speler heeft die eigenaar is van het veld waar de pion nu op staat
            foreach (var ed in _eigendomsbewijzen)
            {
                if (CheckOfStadOpVeldZelfdeIsAlsOpEigendomsbewijs(ed, nieuwPionVeld, veldEigenaar)) straatEigendommen++;
                if (CheckOfNutsOpVeldZelfdeIsAlsOpEigendomsbewijs(ed, nieuwPionVeld, veldEigenaar)) nutsEigendommen++;
                if (CheckOfStationOpVeldZelfdeIsAlsOpEigendomsbewijs(ed, nieuwPionVeld, veldEigenaar)) stationEigendommen++;
            }

            BepaalEnBetaalHuurStraatVeld(eigendomsbewijs, aantalStratenPerStad, straatEigendommen, s, veldEigenaar);
            BepaalEnBetaalHuurStationVeld(eigendomsbewijs, stationEigendommen, s, veldEigenaar);
            BepaalEnBetaalHuurNutsVeld(eigendomsbewijs, nutsEigendommen, s, veldEigenaar);
        }

        public void SpelerOnderneemtActieIndienVeldKoopbaarIs(Veld nieuwPionVeld, Speler speler)
        {
            // Check of de pion op een eigendomveld terecht is gekomen
            if (nieuwPionVeld.IsKoopbaar())
            {
                // Zoek het eigendomsbewijs op dat bij het veld hoort
                var eigendomsbewijs = ZoekVeldEigendomsbewijs(nieuwPionVeld);

                // Bepaal wie de eigenaar is van het veld
                var veldEigenaar = eigendomsbewijs.GetEigenaar();
                if (speler == veldEigenaar) return;
                if (veldEigenaar == null)
                {
                    SpelerBeslistEnMaaktAankoopStraat(speler, eigendomsbewijs);
                }
                else
                {
                    BetaalHuurAanEigenaar(veldEigenaar, nieuwPionVeld, eigendomsbewijs, speler);
                }
            }
        }

        public void BetaalBelastingIndienNodig(Veld nieuwPionVeld, Speler speler)
        {
            if (nieuwPionVeld.GetType() == typeof(BelastingVeld))
            {
                BelastingVeld belastingVeld = (BelastingVeld)_monopolyBord.Velden[nieuwPionVeld.GetVeldPositie()];
                speler.BetaalBedrag(belastingVeld.GetBoete());
            }
        }

        public Veld SpelerPionLatenVerzetten(Worp worp, Speler speler)
        {
            speler.VerzetPion(worp.GetTotaalAantalOgen());

            var nieuwPionVeld = speler.GetPionVeld();

            if (speler.LangsStartGekomen) { _heraBank.BetaalSalaris(speler); }

            StuurSpelerDirectNaarDeGevangenisIndienNodig(speler, nieuwPionVeld);
            return nieuwPionVeld;
        }

        public void StuurSpelerDirectNaarDeGevangenisIndienNodig(Speler speler, Veld veld)
        {
            if (veld.GetVeldPositie() == 30)
            {
                Console.WriteLine(speler.GetNaam() + " gaat direct naar de gevangenis en gaat niet langs start!!!");
                speler.GaDirectNaarDeGevangenis(_monopolyBord.Velden[10]);
            }
        }

        public void SpeelAantalBeurten(int aantalBeurten)
        {
            for (int i = 0; i < aantalBeurten; i++)
            {
                foreach (var speler in _spelers)
                {
                    int aantalDubbels = 0;
                    var spelerIsNogSteedsAanDeBeurt = true;

                    Console.WriteLine("Start van speelronde (" + (i + 1) + ") voor speler " + speler.GetNaam());
                    Console.WriteLine(speler.GetNaam() + " heeft een banksaldo van " + speler.GetSaldo() + " euro");

                    while (spelerIsNogSteedsAanDeBeurt)
                    {
                        var worp = new Worp(_dobbelstenen);

                        Console.WriteLine(speler.GetNaam() + " heeft " + worp.GetTotaalAantalOgen() + " gegooid");

                        // Zo lang als de speler niet in de gevangenis zit, kan hij een pion gaan verzetten
                        if (speler._pion.GetGevangenSatus() == false)
                        {
                            var nieuwPionVeld = SpelerPionLatenVerzetten(worp, speler);

                            BetaalBelastingIndienNodig(nieuwPionVeld, speler);
                            SpelerOnderneemtActieIndienVeldKoopbaarIs(nieuwPionVeld, speler);
                        }

                        // De beurt eindigt als er geen dubbel gegooid is
                        if (worp.DubbelGegooid() == false)
                        {
                            spelerIsNogSteedsAanDeBeurt = false;
                        }
                        // Als er wel een dubbel gegooid is, dan gaan we kijken of de speler naar de gevangenis moet of er juist weer uit mag
                        else
                        {
                            Console.WriteLine("De laatste worp van " + speler.GetNaam() + " was dubbel " + (worp.GetTotaalAantalOgen() / 2));

                            // Als de speler in de gevangenis zit, dan mag hij deze verlaten door het aantal stappen te zetten van de gegooide ogen
                            if (speler._pion.GetGevangenSatus())
                            {
                                Console.WriteLine(speler.GetNaam() + " mag de gevangenis verlaten...");
                                speler.OntsnapUitDeGevangenis(worp.GetTotaalAantalOgen());
                                spelerIsNogSteedsAanDeBeurt = false;
                            }

                            aantalDubbels++;
                            if (aantalDubbels == 3)
                            {
                                Console.WriteLine(speler.GetNaam() + " gaat na 3x dubbel gooien naar de gevangenis!!!");
                                speler.GaDirectNaarDeGevangenis(_monopolyBord.Velden[10]);
                                spelerIsNogSteedsAanDeBeurt = false;
                                aantalDubbels = 0;
                            }
                        }
                    }
                    Console.WriteLine(speler.GetNaam() + " heeft een banksaldo van " + speler.GetSaldo() + " euro");
                    Console.WriteLine("Einde van speelronde (" + (i + 1) + ") voor speler " + speler.GetNaam());
                    Console.WriteLine();
                }
            }
        }

        private void LaatEigendommenVanSpelerOpSchermZien(Speler speler)
        {
            foreach (var eigendomsbewijs in _eigendomsbewijzen)
            {
                if (eigendomsbewijs.GetEigenaar() == speler)
                {
                    Console.Write("(" + eigendomsbewijs.GetVeldPositie() + ") ");
                    Console.WriteLine(eigendomsbewijs.GetVeldNaam());
                }
            }
        }

        public void LaatResultatenZien(int aantalBeurten)
        {
            Console.WriteLine("Na " + aantalBeurten + " beurten weten we het volgende over de spelers:");
            Console.WriteLine();

            foreach (var speler in _spelers)
            {
                Console.WriteLine(speler.GetNaam());
                Console.WriteLine("Heeft een banksaldo van " + speler.GetSaldo() + " euro");
                Console.WriteLine("Staat op veld " + speler.GetPionVeld().GetVeldNaam());
                Console.WriteLine("Bezit de volgende eigendomsbewijzen:");
                LaatEigendommenVanSpelerOpSchermZien(speler);
                Console.WriteLine();
            }

            Console.ReadKey();
        }
    }
}


